package common.server.quartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author Administrator
 * @version [版本号, 2017/11/29 0029]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class QuartzManager {

    /**
     *
     */
    private static StdSchedulerFactory  sf = new StdSchedulerFactory();

    /**
     * 日志部分
     */
    protected static final Logger logger = LoggerFactory.getLogger(QuartzManager.class);


    /**
     *
     * @param quartzProperties
     */
    public static void initSchedulerFactory(Properties quartzProperties){
        try {
            if(null != quartzProperties){
                sf.initialize(quartzProperties);
            }
        } catch (SchedulerException e) {
            logger.error("initSchedulerFactory error:",e);
        }
    }

    /**
     * 执行一次
     *
     * @param info
     */
    public static void addOnce(JobInfo info) {
        call((scheduler) -> {
            try {
                JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
                        .withIdentity(info.getName(), info.getGroup()).build();
                jobDetail.getJobDataMap().put("JobInfo", info);
                scheduler.scheduleJob(jobDetail, TriggerBuilder.newTrigger()
                        .withIdentity(info.getName(), info.getGroup())
                        .startAt(new Date())
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                                .withIntervalInSeconds(3).withRepeatCount(0))
                        .build());
                if (!scheduler.isShutdown()){
                    scheduler.start();
                }

            } catch (SchedulerException e) {
                logger.error("addOrUpdate Error:", e);
            }
        });
    }

    /**
     *  获取单个job信息
     * @param name
     * @param group
     * @return
     */
    public static JobInfo get(String name,String group){
        List<JobInfo> all = getAll();
        final JobInfo[] b = {null};
        all.forEach((jb)->{
            if(jb.getName().equalsIgnoreCase(name) && jb.getGroup().equalsIgnoreCase(group)){
                b[0] =jb;
                return;
            }
        });
        return b[0];
    }

    /**
     * 获取正在运行的job
     * @return
     */
    public static List<JobInfo> getAllRun() {
        return call((scheduler) -> {
            List<JobInfo> jobList = new ArrayList<>();
            try {
                List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
                for (JobExecutionContext executingJob : executingJobs) {
                    JobInfo job = new JobInfo();
                    JobDetail jobDetail = executingJob.getJobDetail();
                    JobKey jobKey = jobDetail.getKey();
                    Trigger trigger = executingJob.getTrigger();
                    job.setGroup(jobKey.getGroup()).setDesc("触发器:" + trigger.getKey()).setName(jobKey.getName());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.setStatus(triggerState.name());
                    if (trigger instanceof CronTrigger)
                    {
                        job.setCron(((CronTrigger) trigger).getCronExpression());
                    }

                    jobList.add(job);
                }
            } catch (SchedulerException e) {
                logger.error("getAll Error:", e);
            }
            return jobList;
        },new ArrayList<JobInfo>());
    }

    /**
     * 获取所有的定时任务信息
     *
     * @return
     */
    public static List<JobInfo> getAll() {
        return call((scheduler) -> {
            List<JobInfo> jobList = new ArrayList<>();
            try {
                GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
                Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
                for (JobKey jobKey : jobKeys) {
                    //获取定时器集合
                    List<? extends Trigger> triggers = getTriggersOfJob(scheduler, jobKey);
                    for (Trigger trigger : triggers) {
                        JobInfo info = (JobInfo)trigger.getJobDataMap().get("JobInfo");
                        System.out.println("Info:" + info);
                        JobInfo job = new JobInfo();
                        job.setGroup(jobKey.getGroup()).setDesc("触发器:" + trigger.getKey()).setName(jobKey.getName());
                        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                        job.setStatus(triggerState.name());
                        if (trigger instanceof CronTrigger){
                            job.setCron(((CronTrigger) trigger).getCronExpression());
                        }
                        jobList.add(job);
                    }
                }
            } catch (SchedulerException e) {
                logger.error("getAll Error:", e);
            }
            return jobList;
        },new ArrayList<JobInfo>());
    }



    /**
     * 添加,更新定时任务
     *
     * @param info
     */
    public static void addOrUpdate(JobInfo info) {
        call((scheduler) -> {
            try {
                TriggerKey triggerKey = TriggerKey.triggerKey(info.getName(),
                        info.getGroup());
                CronTrigger trigger = getTrigger(scheduler, triggerKey);
                if (null == trigger) {
                    // 任务名，任务组，任务执行类
                    // 按新的cron表达式构建一个新的trigger
                    JobDetail jobDetail = JobBuilder
                            .newJob(QuartzJobFactory.class)
                            .withIdentity(info.getName(), info.getGroup())
                            .build();
                    jobDetail.getJobDataMap().put("JobInfo", info);

                    trigger = TriggerBuilder.newTrigger()
                            .withIdentity(info.getName(), info.getGroup())
                            .withSchedule(CronScheduleBuilder
                                    .cronSchedule(info.getCron()))
                            .build();
                    scheduler.scheduleJob(jobDetail, trigger);
                } else {
                    // 按新的cron表达式重新构建trigger
                    // 按新的trigger重新设置job执行
                    trigger = trigger.getTriggerBuilder()
                            .withIdentity(triggerKey)
                            .withSchedule(CronScheduleBuilder
                                    .cronSchedule(info.getCron()))
                            .build();
                    scheduler.rescheduleJob(triggerKey, trigger);
                }
                if (!scheduler.isShutdown()){
                    scheduler.start();
                }

            } catch (SchedulerException e) {
                logger.error("addOrUpdate Error:", e);
            }
        });
    }

    /**
     * 移除定时任务
     *
     * @param info
     */
    public static void remove(JobInfo info) {
        call((scheduler) -> {
            try {
                logger.info("Remove Job:{}",info);
                // 删除任务后，所对应的trigger也将被删除
                JobKey jobKey = JobKey.jobKey(info.getName(), info.getGroup());
                scheduler.deleteJob(jobKey);
            } catch (SchedulerException e) {
                logger.error("remove Error:", e);
            }
        });
    }

    /**
     * 暂停定时任务
     *
     * @param info
     */
    public static void pause(JobInfo info) {
        call((scheduler) -> {
            try {
                scheduler.pauseJob(
                        JobKey.jobKey(info.getName(), info.getGroup()));
            } catch (SchedulerException e) {
                logger.error("pause Error:", e);
            }
        });
    }

    /**
     * 恢复定时任务
     *
     * @param info
     */
    public static void resume(JobInfo info) {
        call((scheduler) -> {
            try {
                scheduler.resumeJob(
                        JobKey.jobKey(info.getName(), info.getGroup()));
            } catch (SchedulerException e) {
                logger.error("resume Error:", e);
            }
        });
    }

    /**
     * 执行一次任务
     *
     * @param info
     */
    public static void once(JobInfo info) {
        call((scheduler) -> {
            try {
                scheduler.triggerJob(
                        JobKey.jobKey(info.getName(), info.getGroup()));
            } catch (SchedulerException e) {
                logger.error("remove Error:", e);
            }
        });
    }

    /**
     * 获取定时器信息
     *
     * @param scheduler
     * @param triggerKey
     * @return
     * @throws SchedulerException
     */
    private static CronTrigger getTrigger(Scheduler scheduler,
                                          TriggerKey triggerKey) throws SchedulerException {
        return (CronTrigger) scheduler.getTrigger(triggerKey);
    }

    /**
     *
     * @param scheduler
     * @param jobKey
     * @return
     * @throws SchedulerException
     */
    private static List<? extends Trigger> getTriggersOfJob(Scheduler scheduler,
                                                            JobKey jobKey) throws SchedulerException {
        return scheduler.getTriggersOfJob(jobKey);
    }

    /**
     * @param function
     * @param d
     * @param <R>
     * @return
     */
    private static <R> R call(Function<Scheduler, R> function, R d) {
        try {
            return function.apply(sf.getScheduler());
        } catch (Exception e) {
            logger.error("call Error:", e);
        }
        return d;
    }

    /**
     * @param consumer
     */
    private static void call(Consumer<Scheduler> consumer) {
        try {
            consumer.accept(sf.getScheduler());
        } catch (Exception e) {
            logger.error("call Error:", e);
        }
    }
}
